home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / hobby / ast44src.zip / XGENERAL.C < prev    next >
C/C++ Source or Header  |  1995-02-11  |  24KB  |  840 lines

  1. /*
  2. ** Astrolog (Version 4.40) File: xgeneral.c
  3. **
  4. ** IMPORTANT NOTICE: The graphics database and chart display routines
  5. ** used in this program are Copyright (C) 1991-1995 by Walter D. Pullen
  6. ** (astara@u.washington.edu). Permission is granted to freely use and
  7. ** distribute these routines provided one doesn't sell, restrict, or
  8. ** profit from them in any way. Modification is allowed provided these
  9. ** notices remain with any altered or edited versions of the program.
  10. **
  11. ** The main planetary calculation routines used in this program have
  12. ** been Copyrighted and the core of this program is basically a
  13. ** conversion to C of the routines created by James Neely as listed in
  14. ** Michael Erlewine's 'Manual of Computer Programming for Astrologers',
  15. ** available from Matrix Software. The copyright gives us permission to
  16. ** use the routines for personal use but not to sell them or profit from
  17. ** them in any way.
  18. **
  19. ** The PostScript code within the core graphics routines are programmed
  20. ** and Copyright (C) 1992-1993 by Brian D. Willoughby
  21. ** (brianw@sounds.wa.com). Conditions are identical to those above.
  22. **
  23. ** The extended accurate ephemeris databases and formulas are from the
  24. ** calculation routines in the program "Placalc" and are programmed and
  25. ** Copyright (C) 1989,1991,1993 by Astrodienst AG and Alois Treindl
  26. ** (alois@azur.ch). The use of that source code is subject to
  27. ** regulations made by Astrodienst Zurich, and the code is not in the
  28. ** public domain. This copyright notice must not be changed or removed
  29. ** by any user of this program.
  30. **
  31. ** Initial programming 8/28,30, 9/10,13,16,20,23, 10/3,6,7, 11/7,10,21/1991.
  32. ** X Window graphics initially programmed 10/23-29/1991.
  33. ** PostScript graphics initially programmed 11/29-30/1992.
  34. ** Last code change made 1/29/1995.
  35. */
  36.  
  37. #include "astrolog.h"
  38.  
  39.  
  40. #ifdef GRAPH
  41. /*
  42. ******************************************************************************
  43. ** Core Graphic Procedures.
  44. ******************************************************************************
  45. */
  46.  
  47. /* Set the current color to use in drawing on the screen or bitmap array. */
  48.  
  49. void DrawColor(col)
  50. KI col;
  51. {
  52.   if (gi.fFile) {
  53. #ifdef PS
  54.     if (gs.fPS) {
  55.       if (gi.kiCur != col) {
  56.         PsStrokeForce();      /* Render existing path with current color */
  57.         fprintf(gi.file, "%.2f %.2f %.2f c\n",
  58.           (real)RGBR(rgbbmp[col])/255.0, (real)RGBG(rgbbmp[col])/255.0,
  59.           (real)RGBB(rgbbmp[col])/255.0);
  60.       }
  61.     }
  62. #endif
  63. #ifdef META
  64.     if (gs.fMeta)
  65.       gi.kiLineDes = col;
  66. #endif
  67.   }
  68. #ifdef X11
  69.   else
  70.     XSetForeground(gi.disp, gi.gc, rgbind[col]);
  71. #endif
  72. #ifdef MSG
  73.   else
  74.     _setcolor(col);
  75. #endif
  76. #ifdef BGI
  77.   else
  78.     setcolor(col);
  79. #endif
  80.   gi.kiCur = col;
  81. }
  82.  
  83.  
  84. /* Set a single point on the screen. This is the most basic graphic function */
  85. /* and is called by all the more complex routines. Based on what mode we are */
  86. /* in, we either set a cell in the bitmap array or a pixel on the window.    */
  87.  
  88. void DrawPoint(x, y)
  89. int x, y;
  90. {
  91.   if (gi.fFile) {
  92.     if (gs.fBitmap) {
  93.       /* Force the coordinates to be within the bounds of the bitmap array. */
  94.  
  95.       if (x < 0)
  96.         x = 0;
  97.       else if (x >= gs.xWin)
  98.         x = gs.xWin-1;
  99.       if (y < 0)
  100.         y = 0;
  101.       else if (y >= gs.yWin)
  102.         y = gs.yWin-1;
  103.       if (gi.yBand) {
  104.         y -= gi.yOffset;
  105.         if (y < 0 || y >= gi.yBand)
  106.           return;
  107.       }
  108.       BmSet(gi.bm, x, y, gi.kiCur);
  109.     }
  110. #ifdef PS
  111.     else if (gs.fPS) {
  112.       DrawColor(gi.kiCur);
  113.       PsLineCap(fTrue);
  114.       fprintf(gi.file, "%d %d d\n", x, y);
  115.       PsStroke(2);
  116.     }
  117. #endif
  118. #ifdef META
  119.     else {
  120.       gi.kiFillDes = gi.kiCur;
  121.       MetaSelect();
  122.       MetaEllipse(x-gi.nPenWid/2, y-gi.nPenWid/2,
  123.         x+gi.nPenWid/2, y+gi.nPenWid/2);
  124.     }
  125. #endif
  126.   }
  127. #ifdef X11
  128.   else
  129.     XDrawPoint(gi.disp, gi.pmap, gi.gc, x, y);
  130. #endif
  131. #ifdef MSG
  132.   else
  133.     _setpixel(gi.xOffset + x, gi.yOffset + y);
  134. #endif
  135. #ifdef BGI
  136.   else
  137.     putpixel(gi.xOffset + x, gi.yOffset + y, gi.kiCur);
  138. #endif
  139. }
  140.  
  141.  
  142. /* Draw dot a little larger than just a single pixel at specified location. */
  143.  
  144. void DrawSpot(x, y)
  145. int x, y;
  146. {
  147. #ifdef PS
  148.   if (gs.fPS) {
  149.     PsLineWidth((int)(gi.rLineWid*3.0));
  150.     DrawPoint(x, y);
  151.     PsLineWidth((int)(gi.rLineWid/3.0));
  152.     return;
  153.   }
  154. #endif
  155. #ifdef META
  156.   if (gs.fMeta) {
  157.     gi.kiFillDes = gi.kiCur;
  158.     MetaSelect();
  159.     MetaEllipse(x-gi.nPenWid, y-gi.nPenWid, x+gi.nPenWid, y+gi.nPenWid);
  160.     return;
  161.   }
  162. #endif
  163.   DrawPoint(x, y);
  164.   DrawPoint(x, y-1);
  165.   DrawPoint(x-1, y);
  166.   DrawPoint(x+1, y);
  167.   DrawPoint(x, y+1);
  168. }
  169.  
  170.  
  171. /* Draw a filled in block, defined by the corners of its rectangle. */
  172.  
  173. void DrawBlock(x1, y1, x2, y2)
  174. int x1, y1, x2, y2;
  175. {
  176.   int x, y;
  177.  
  178.   if (gi.fFile) {
  179.     if (gs.fBitmap) {
  180.       if (gi.yBand) {
  181.         y1 -= gi.yOffset;
  182.         if (y1 < 0)
  183.           y1 = 0;
  184.         y2 -= gi.yOffset;
  185.         if (y2 > gi.yBand)
  186.           y2 = gi.yBand-1;
  187.       }
  188.       for (y = y1; y <= y2; y++)           /* For bitmap, we have to  */
  189.         for (x = x1; x <= x2; x++)         /* just fill in the array. */
  190.           BmSet(gi.bm, x, y, gi.kiCur);
  191.     }
  192. #ifdef PS
  193.     else if (gs.fPS) {
  194.       DrawColor(gi.kiCur);
  195.       fprintf(gi.file, "%d %d %d %d rf\n",
  196.         Max(x1-gi.nPenWid/4, 0), Max(y1-gi.nPenWid/4, 0),
  197.         x2-x1+gi.nPenWid/4, y2-y1+gi.nPenWid/4);
  198.     }
  199. #endif
  200. #ifdef META
  201.     else {
  202.       gi.kiFillDes = gi.kiCur;
  203.       MetaSelect();
  204.       MetaRectangle(x1-gi.nPenWid/2, y1-gi.nPenWid/2,
  205.         x2+gi.nPenWid/2, y2+gi.nPenWid/2);
  206.     }
  207. #endif
  208.   }
  209. #ifdef X11
  210.   else
  211.     XFillRectangle(gi.disp, gi.pmap, gi.gc, x1, y1, x2-x1, y2-y1);
  212. #endif
  213. #ifdef MSG
  214.   else
  215.     _rectangle(_GFILLINTERIOR,
  216.       gi.xOffset + x1, gi.yOffset + y1, gi.xOffset + x2, gi.yOffset + y2);
  217. #endif
  218. #ifdef BGI
  219.   else {
  220.     setfillstyle(SOLID_FILL, gi.kiCur);
  221.     bar(gi.xOffset + x1, gi.yOffset + y1, gi.xOffset + x2, gi.yOffset + y2);
  222.   }
  223. #endif
  224. }
  225.  
  226.  
  227. /* Draw a rectangle on the screen with specified thickness. This is just   */
  228. /* like DrawBlock() except that we are only drawing the edges of the area. */
  229.  
  230. void DrawBox(x1, y1, x2, y2, xsiz, ysiz)
  231. int x1, y1, x2, y2, xsiz, ysiz;
  232. {
  233. #ifdef META
  234.   if (gs.fMeta)
  235.     /* For thin boxes in metafiles, we can just output one rectangle record */
  236.     /* instead of drawing each side separately as we have to do otherwise.  */
  237.     if (xsiz <= 1 && ysiz <= 1) {
  238.       gi.kiFillDes = kNull;          /* Specify a hollow fill brush. */
  239.       MetaSelect();
  240.       MetaRectangle(x1, y1, x2, y2);
  241.       return;
  242.     }
  243. #endif
  244.   DrawBlock(x1, y1, x2, y1 + ysiz - 1);
  245.   DrawBlock(x1, y1 + ysiz, x1 + xsiz - 1, y2 - ysiz);
  246.   DrawBlock(x2 - xsiz + 1, y1 + ysiz, x2, y2 - ysiz);
  247.   DrawBlock(x1, y2 - ysiz + 1, x2, y2);
  248. }
  249.  
  250.  
  251. /* Clear and erase the graphics screen or bitmap contents. */
  252.  
  253. void DrawClearScreen()
  254. {
  255. #ifdef PS
  256.   if (gs.fPS) {
  257.     /* For PostScript charts first output page orientation information. */
  258.     if (!gi.fEps) {
  259.       if (gs.nOrient == 0)
  260.         gs.nOrient = gs.xWin > gs.yWin ? -1 : 1;
  261.       if (gs.nOrient < 0) {
  262.         /* chartx and charty are reversed for Landscape mode. */
  263.         fprintf(gi.file, "%d %d translate\n",
  264.           ((int)(gs.xInch*72.0+rRound) + gs.yWin)/2,
  265.           ((int)(gs.yInch*72.0+rRound) + gs.xWin)/2);
  266.         fprintf(gi.file, "-90 rotate\n");
  267.       } else {
  268.         /* Most charts are in Portrait mode */
  269.         fprintf(gi.file, "%d %d translate\n",
  270.           ((int)(gs.xInch*72.0+rRound) - gs.xWin)/2,
  271.           ((int)(gs.yInch*72.0+rRound) + gs.yWin)/2);
  272.       }
  273.     } else
  274.       fprintf(gi.file, "0 %d translate\n", gs.yWin);
  275.     fprintf(gi.file, "1 -1 scale\n");
  276.     gs.nScale *= PSMUL; gs.xWin *= PSMUL; gs.yWin *= PSMUL; gi.nScale *= PSMUL;
  277.     fprintf(gi.file, "1 %d div dup scale\n", PSMUL);
  278.   }
  279. #endif
  280. #ifdef META
  281.   if (gs.fMeta)
  282.     MetaInit();    /* For metafiles first go write our header information. */
  283. #endif
  284.  
  285.   /* Don't actually erase the screen if the -Xj switch is in effect. */
  286.   if (gs.fJetTrail)
  287.     return;
  288.  
  289. #ifdef MSG
  290.   if (!gi.fFile)
  291.     _clearscreen(_GCLEARSCREEN);
  292. #endif
  293. #ifdef BGI
  294.   if (!gi.fFile)
  295.     clearviewport();
  296. #endif
  297.   DrawColor(gi.kiOff);
  298.   DrawBlock(0, 0, gs.xWin - 1, gs.yWin - 1);    /* Clear bitmap screen. */
  299. }
  300.  
  301.  
  302. /* Draw a line on the screen, specified by its endpoints. In addition, we */
  303. /* have specified a skip factor, which allows us to draw dashed lines.    */
  304.  
  305. void DrawDash(x1, y1, x2, y2, skip)
  306. int x1, y1, x2, y2, skip;
  307. {
  308.   int x = x1, y = y1, xadd, yadd, yinc, xabs, yabs, i, j = 0;
  309.  
  310.   if (skip < 0)
  311.     skip = 0;
  312. #ifdef ISG
  313.   if (!gi.fFile) {
  314.     if (!skip) {
  315. #ifdef X11
  316.       /* For non-dashed X window lines, let's have the Xlib do it for us. */
  317.  
  318.       XDrawLine(gi.disp, gi.pmap, gi.gc, x1, y1, x2, y2);
  319. #else
  320.       /* For non-dashed lines, let's have the graphics library do it for us. */
  321.  
  322.       PcMoveTo(gi.xOffset + x1, gi.yOffset + y1);
  323.       PcLineTo(gi.xOffset + x2, gi.yOffset + y2);
  324. #endif
  325.       return;
  326.     }
  327.   }
  328. #endif /* ISG */
  329.  
  330. #ifdef PS
  331.   if (gs.fPS) {
  332.  
  333.     /* For PostScript charts we can save file size if we output a LineTo  */
  334.     /* command when the start vertex is the same as the end vertex of the */
  335.     /* previous line drawn, instead of writing out both vertices.         */
  336.  
  337.     PsLineCap(fTrue);
  338.     PsDash(skip);
  339.     if (gi.xPen != x1 || gi.yPen != y1)
  340.       fprintf(gi.file, "%d %d %d %d l\n", x1, y1, x2, y2);
  341.     else
  342.       fprintf(gi.file, "%d %d t\n", x2, y2);
  343.     gi.xPen = x2; gi.yPen = y2;
  344.     PsStroke(2);
  345.     return;
  346.   }
  347. #endif
  348.  
  349. #ifdef META
  350.   if (gs.fMeta) {
  351.  
  352.     /* For metafile charts we can really save file size for consecutive */
  353.     /* lines sharing endpoints by consolidating them into a PolyLine.   */
  354.  
  355.     if (gi.xPen != x1 || gi.yPen != y1) {
  356.       gi.kiLineDes = (gi.kiLineDes & 15) + 16*(skip > 3 ? 3 : skip);
  357.       MetaSelect();
  358.       gi.pwPoly = gi.pwMetaCur;
  359.       MetaRecord(8, 0x325);      /* Polyline */
  360.       MetaWord(2); MetaWord(x1); MetaWord(y1);
  361.     } else {
  362.       *gi.pwPoly += 2;
  363.       (*(gi.pwPoly+3))++;
  364.       /* Note: We should technically update the max record size in the   */
  365.       /* file header if need be here too, but it doesn't seem necessary. */
  366.     }
  367.     MetaWord(x2); MetaWord(y2);
  368.     gi.xPen = x2; gi.yPen = y2;
  369.     return;
  370.   }
  371. #endif
  372.  
  373.   /* If none of the above cases hold, we have to draw the line dot by dot. */
  374.  
  375.   xadd = x2 - x1 >= 0 ? 1 : 3;
  376.   yadd = y2 - y1 >= 0 ? 2 : 4;
  377.   xabs = abs(x2 - x1);
  378.   yabs = abs(y2 - y1);
  379.  
  380.   /* Technically what we're doing here is drawing a line which is more    */
  381.   /* horizontal then vertical. We always increment x by 1, and increment  */
  382.   /* y whenever a fractional variable passes a certain amount. For lines  */
  383.   /* that are more vertical than horizontal, we just swap x and y coords. */
  384.  
  385.   if (xabs < yabs) {
  386.     SwapN(xadd, yadd);
  387.     SwapN(xabs, yabs);
  388.   }
  389.   yinc = (xabs >> 1) - ((xabs & 1 ^ 1) && xadd > 2);
  390.   for (i = xabs + 1; i; i--) {
  391.     if (j < 1)
  392.       DrawPoint(x, y);
  393.     j = j < skip ? j+1 : 0;
  394.     switch (xadd) {
  395.     case 1: x++; break;
  396.     case 2: y++; break;
  397.     case 3: x--; break;
  398.     case 4: y--; break;
  399.     }
  400.     yinc += yabs;
  401.     if (yinc - xabs >= 0) {
  402.       yinc -= xabs;
  403.       switch (yadd) {
  404.       case 1: x++; break;
  405.       case 2: y++; break;
  406.       case 3: x--; break;
  407.       case 4: y--; break;
  408.       }
  409.     }
  410.   }
  411. }
  412.  
  413.  
  414. /* Draw a normal line on the screen; however, if the x coordinates are close */
  415. /* to either of the two given bounds, then we assume that the line runs off  */
  416. /* one side and reappears on the other, so draw the appropriate two lines    */
  417. /* instead. This is used by the Ley line and astro-graph routines, which     */
  418. /* draw lines running around the world and hence off the edges of the maps.  */
  419.  
  420. void DrawWrap(x1, y1, x2, y2, xmin, xmax)
  421. int x1, y1, x2, y2, xmin, xmax;
  422. {
  423.   int xmid, ymid, i;
  424.  
  425.   if (x1 < 0) {           /* Special case for drawing world map. */
  426.     DrawPoint(x2, y2);
  427.     return;
  428.   }
  429.   xmid = (xmax-xmin) / 2;
  430.  
  431.   /* If endpoints aren't near opposite edges, just draw the line and return. */
  432.  
  433.   if (abs(x2-x1) < xmid) {
  434.     DrawLine(x1, y1, x2, y2);
  435.     return;
  436.   }
  437.   if ((i = (xmax-xmin+1) + (x1 < xmid ? x1-x2 : x2-x1)) == 0)
  438.     i = 1;
  439.  
  440.   /* Determine vertical coordinate where our line runs off edges of screen. */
  441.  
  442.   ymid = y1+(int)((real)(y2-y1)*
  443.     (x1 < xmid ? (real)(x1-xmin) : (real)(xmax-x1))/(real)i + rRound);
  444.   DrawLine(x1, y1, x1 < xmid ? xmin : xmax, ymid);
  445.   DrawLine(x2 < xmid ? xmin : xmax, ymid, x2, y2);
  446. }
  447.  
  448.  
  449. /* This routine, and its companion below, clips a line defined by its  */
  450. /* endpoints to either above some line y=c, or below some line y=c. By */
  451. /* passing in parameters in different orders, we can clip to vertical  */
  452. /* lines, too. These are used by the DrawClip() routine below.         */
  453.  
  454. void ClipLesser(x1, y1, x2, y2, s)
  455. int *x1, *y1, *x2, *y2, s;
  456. {
  457.   *x1 -= (int)((long)(*y1-s)*(*x2-*x1)/(*y2-*y1));
  458.   *y1 = s;
  459. }
  460.  
  461. void ClipGreater(x1, y1, x2, y2, s)
  462. int *x1, *y1, *x2, *y2, s;
  463. {
  464.   *x1 += (int)((long)(s-*y1)*(*x2-*x1)/(*y2-*y1));
  465.   *y1 = s;
  466. }
  467.  
  468.  
  469. /* Draw a line on the screen. This is just like DrawLine() routine earlier; */
  470. /* however, first clip the endpoints to the given viewport before drawing.  */
  471.  
  472. void DrawClip(x1, y1, x2, y2, xl, yl, xh, yh, skip)
  473. int x1, y1, x2, y2, xl, yl, xh, yh, skip;
  474. {
  475.   if (x1 < xl)
  476.     ClipLesser (&y1, &x1, &y2, &x2, xl);    /* Check left side of window. */
  477.   if (x2 < xl)
  478.     ClipLesser (&y2, &x2, &y1, &x1, xl);
  479.   if (y1 < yl)
  480.     ClipLesser (&x1, &y1, &x2, &y2, yl);    /* Check top side of window.  */
  481.   if (y2 < yl)
  482.     ClipLesser (&x2, &y2, &x1, &y1, yl);
  483.   if (x1 > xh)
  484.     ClipGreater(&y1, &x1, &y2, &x2, xh);    /* Check right of window.  */
  485.   if (x2 > xh)
  486.     ClipGreater(&y2, &x2, &y1, &x1, xh);
  487.   if (y1 > yh)
  488.     ClipGreater(&x1, &y1, &x2, &y2, yh);    /* Check bottom of window. */
  489.   if (y2 > yh)
  490.     ClipGreater(&x2, &y2, &x1, &y1, yh);
  491.   DrawDash(x1, y1, x2, y2, skip);           /* Go draw the line.       */
  492. }
  493.  
  494.  
  495. /* Draw a circle or ellipse inside the given bounding rectangle. */
  496.  
  497. void DrawEllipse(x1, y1, x2, y2)
  498. int x1, y1, x2, y2;
  499. {
  500.   int x, y, rx, ry, m, n, u, v, i;
  501.  
  502.   if (gi.fFile) {
  503.     x = (x1+x2)/2; y = (y1+y2)/2; rx = (x2-x1)/2; ry = (y2-y1)/2;
  504.     if (gs.fBitmap) {
  505.       m = x + rx; n = y;
  506.       for (i = 0; i <= nDegMax; i += DEGINC) {
  507.         u = x + (int)(((real)rx+rRound)*RCosD((real)i));
  508.         v = y + (int)(((real)ry+rRound)*RSinD((real)i));
  509.         DrawLine(m, n, u, v);
  510.         m = u; n = v;
  511.       }
  512.     }
  513. #ifdef PS
  514.     else if (gs.fPS) {
  515.       PsLineCap(fFalse);
  516.       PsStrokeForce();
  517.       PsDash(0);
  518.       fprintf(gi.file, "%d %d %d %d el\n", rx, ry, x, y);
  519.     }
  520. #endif
  521. #ifdef META
  522.     else {
  523.       gi.kiFillDes = kNull;    /* Specify a hollow fill brush. */
  524.       MetaSelect();
  525.       MetaEllipse(x1+gi.nPenWid/3, y1+gi.nPenWid/3,
  526.         x2+gi.nPenWid/3, y2+gi.nPenWid/3);
  527.     }
  528. #endif
  529.   }
  530. #ifdef X11
  531.   else
  532.     XDrawArc(gi.disp, gi.pmap, gi.gc, x1, y1, x2-x1, y2-y1, 0, nDegMax*64);
  533. #endif
  534. #ifdef MSG
  535.   else
  536.     _ellipse(_GBORDER, gi.xOffset + x1, gi.yOffset + y1,
  537.       gi.xOffset + x2, gi.yOffset + y2);
  538. #endif
  539. #ifdef BGI
  540.   else
  541.     ellipse(gi.xOffset + (x1+x2)/2, gi.yOffset + (y1+y2)/2,
  542.       0, 360, (x2-x1)/2, (y2-y1)/2);
  543. #endif
  544. }
  545.  
  546.  
  547. /* Print a string of text on the graphic window at specified location. To  */
  548. /* do this we either use Astrolog's own "font" (6x10) and draw each letter */
  549. /* separately, or else specify system fonts for PostScript and metafiles.  */
  550.  
  551. void DrawSz(sz, x, y, dt)
  552. CONST char *sz;
  553. int x, y, dt;
  554. {
  555.   int s = gi.nScale, c = gi.kiCur, cch;
  556.  
  557.   cch = CchSz(sz);
  558.   if (!(dt & dtScale))
  559.     gi.nScale = gi.nScaleT;
  560.   x += gi.nScale;
  561.   if (!(dt & dtLeft))
  562.     x -= cch*xFont*gi.nScale/2;
  563.   if (dt & dtBottom)
  564.     y -= (yFont-3)*gi.nScale;
  565.   else if (!(dt & dtTop))
  566.     y -= yFont*gi.nScale/2;
  567.   if (dt & dtErase) {
  568.     DrawColor(gi.kiOff);
  569.     DrawBlock(x, y, x+xFont*gi.nScale*cch-1, y+(yFont-2)*gi.nScale);
  570.   }
  571.   DrawColor(c);
  572. #ifdef PS
  573.   if (gs.fPS && gs.fFont) {
  574.     PsFont(4);
  575.     fprintf(gi.file, "%d %d(%s)center\n",
  576.       x + xFont*gi.nScale*cch/2, y + yFont*gi.nScale/2, sz);
  577.     gi.nScale = s;
  578.     return;
  579.   }
  580. #endif
  581.   while (*sz) {
  582. #ifdef META
  583.     if (gs.fMeta && gs.fFont) {
  584.       gi.nFontDes = 3;
  585.       gi.kiTextDes = gi.kiCur;
  586.       gi.nAlignDes = 0x6 | 0 /* Center | Top */;
  587.       MetaSelect();
  588.       MetaTextOut(x, y, 1);
  589.       MetaWord(WFromBB(*sz, 0));
  590.     } else
  591. #endif
  592.       DrawTurtle(szDrawCh[(_char)*sz-' '], x, y);
  593.     x += xFont*gi.nScale;
  594.     sz++;
  595.   }
  596.   gi.nScale = s;
  597. }
  598.  
  599.  
  600. /* Draw the glyph of a sign at particular coordinates on the screen.    */
  601. /* To do this we either use Astrolog's turtle vector representation or  */
  602. /* we may specify a system font character for PostScript and metafiles. */
  603.  
  604. void DrawSign(i, x, y)
  605. int i, x, y;
  606. {
  607. #ifdef PS
  608.   if (gs.fPS && gs.fFont) {
  609.     PsFont(1);
  610.     fprintf(gi.file, "%d %d(%c)center\n", x, y, 'A' + i - 1);
  611.     return;
  612.   }
  613. #endif
  614. #ifdef META
  615.   if (gs.fMeta && gs.fFont) {
  616.     gi.nFontDes = 1;
  617.     gi.kiTextDes = gi.kiCur;
  618.     gi.nAlignDes = 0x6 | 0x8 /* Center | Bottom */;
  619.     MetaSelect();
  620.     MetaTextOut(x, y+4*gi.nScale, 1);
  621.     MetaWord(WFromBB('^' + i - 1, 0));
  622.     return;
  623.   }
  624. #endif
  625.   DrawTurtle(szDrawSign[i], x, y);
  626. }
  627.  
  628.  
  629. /* Draw the number of a house at particular coordinates on the screen. */
  630. /* We either use a turtle vector or write a number in a system font.   */
  631.  
  632. void DrawHouse(i, x, y)
  633. int i, x, y;
  634. {
  635. #ifdef PS
  636.   if (gs.fPS && gs.fFont) {
  637.     PsFont(3);
  638.     fprintf(gi.file, "%d %d(%d)center\n", x, y, i);
  639.     return;
  640.   }
  641. #endif
  642. #ifdef META
  643.   if (gs.fMeta && gs.fFont) {
  644.     gi.nFontDes = 2;
  645.     gi.kiTextDes = gi.kiCur;
  646.     gi.nAlignDes = 0x6 | 0x8 /* Center | Bottom */;
  647.     MetaSelect();
  648.     MetaTextOut(x, y+3*gi.nScale, 1 + (i>9));
  649.     MetaWord(WFromBB(i > 9 ? '1' : '0'+i, i > 9 ? '0'+i-10 : 0));
  650.     return;
  651.   }
  652. #endif
  653.   DrawTurtle(szDrawHouse[i], x, y);
  654. }
  655.  
  656.  
  657. /* Draw the glyph of an object at particular coordinates on the screen. */
  658.  
  659. void DrawObject(obj, x, y)
  660. int obj, x, y;
  661. {
  662.   char szGlyph[4];
  663.  
  664.   if (!gs.fLabel)    /* If we are inhibiting labels, then do nothing. */
  665.     return;
  666.  
  667.   /* For other planet centered charts, we have to remember that that     */
  668.   /* particular planet's index now represents the Earth. If we are given */
  669.   /* that index to draw, then change it so we draw the Earth instead.    */
  670.  
  671.   if (gi.nMode != gOrbit && ((obj == us.objCenter && obj > oMoo) ||
  672.     (us.objCenter == 0 && obj == oSun)))
  673.     obj = 0;
  674.   DrawColor(kObjB[obj]);
  675.   if (obj <= oNorm) {
  676. #ifdef PS
  677.     if (gs.fPS && gs.fFont == 1 && obj < uranLo && szObjectFont[obj] != ' ') {
  678.       PsFont(2);
  679.       fprintf(gi.file, "%d %d(%c)center\n", x, y, szObjectFont[obj]);
  680.       return;
  681.     }
  682. #endif
  683. #ifdef META
  684.     if (gs.fMeta && gs.fFont == 1 &&
  685.       obj < uranLo && szObjectFont[obj] != ' ') {
  686.       gi.nFontDes = 4;
  687.       gi.kiTextDes = gi.kiCur;
  688.       gi.nAlignDes = 0x6 | 0x8 /* Center | Bottom */;
  689.       MetaSelect();
  690.       MetaTextOut(x, y+5*gi.nScale, 1);
  691.       MetaWord(WFromBB(szObjectFont[obj], 0));
  692.       return;
  693.     }
  694. #endif
  695.     DrawTurtle(szDrawObject[obj], x, y);
  696.  
  697.   /* Normally we can just go draw the glyph; however, stars don't have */
  698.   /* glyphs, so for these draw their three letter abbreviation.        */
  699.  
  700.   } else {
  701.     sprintf(szGlyph, "%c%c%c", chObj3(obj));
  702.     DrawSz(szGlyph, x, y, dtCent);
  703.   }
  704. }
  705.  
  706.  
  707. /* Draw the glyph of an aspect at particular coordinates on the screen. */
  708. /* Again we either use Astrolog's turtle vector or a system Astro font. */
  709.  
  710. void DrawAspect(asp, x, y)
  711. int asp, x, y;
  712. {
  713.   CONST char *sz;
  714.  
  715. #ifdef PS
  716.   if (gs.fPS && gs.fFont == 1 && szAspectFont[asp-1] != ' ') {
  717.     PsFont(2);
  718.     fprintf(gi.file, "%d %d(%s%c)center\n", x, y,
  719.       asp == aSSq || asp == aSes ? "\\" : "", szAspectFont[asp-1]);
  720.     return;
  721.   }
  722. #endif
  723. #ifdef META
  724.   if (gs.fMeta && gs.fFont == 1 && szAspectFont[asp-1] != ' ') {
  725.     gi.nFontDes = 4;
  726.     gi.kiTextDes = gi.kiCur;
  727.     gi.nAlignDes = 0x6 | 0x8 /* Center | Bottom */;
  728.     MetaSelect();
  729.     MetaTextOut(x, y+5*gi.nScale, 1);
  730.     MetaWord(WFromBB(szAspectFont[asp-1], 0));
  731.     return;
  732.   }
  733. #endif
  734.   sz = szDrawAspect[asp];
  735.   if (us.fParallel) {
  736.     if (asp == aCon)
  737.       sz = "BU4BLD8BR2U8";
  738.     else if (asp = aOpp)
  739.       sz = "BU4BLD8BR2U8BF3BLL6BD2R6";
  740.   }
  741.   DrawTurtle(sz, x, y);
  742. }
  743.  
  744.  
  745. /* Convert a string segment to a positive number, updating the string to  */
  746. /* point beyond the number chars. Return 1 if the string doesn't point to */
  747. /* a numeric value. This is used by the DrawTurtle() routine to extract   */
  748. /* motion vector quantities from draw strings, e.g. the "12" in "U12".    */
  749.  
  750. int NFromPch(str)
  751. CONST char **str;
  752. {
  753.   int num = 0, i = 0;
  754.  
  755.   loop {
  756.     if (**str < '0' || **str > '9')
  757.       return num > 0 ? num : (i < 1 ? 1 : 0);
  758.     num = num*10+(**str)-'0';
  759.     (*str)++;
  760.     i++;
  761.   }
  762. }
  763.  
  764.  
  765. /* This routine is used to draw complicated objects composed of lots of line */
  766. /* segments on the screen, such as all the glyphs and coastline pieces. It   */
  767. /* is passed in a string of commands defining what to draw in relative       */
  768. /* coordinates. This is a copy of the format of the BASIC draw command found */
  769. /* in PC's. For example, "U5R10D5L10" means go up 5 dots, right 10, down 5,  */
  770. /* and left 10 - draw a box twice as wide as it is high.                     */
  771.  
  772. void DrawTurtle(sz, x0, y0)
  773. CONST char *sz;
  774. int x0, y0;
  775. {
  776.   int i, j, x, y, deltax, deltay;
  777.   bool fBlank, fNoupdate;
  778.   char chCmd;
  779.  
  780.   gi.xTurtle = x0; gi.yTurtle = y0;
  781.   while (chCmd = ChCap(*sz)) {
  782.     sz++;
  783.  
  784.     /* 'B' prefixing a command means just move the cursor, and don't draw. */
  785.  
  786.     if (fBlank = (chCmd == 'B')) {
  787.       chCmd = ChCap(*sz);
  788.       sz++;
  789.     }
  790.  
  791.     /* 'N' prefixing a command means don't update cursor when done drawing. */
  792.  
  793.     if (fNoupdate = (chCmd == 'N')) {
  794.       chCmd = ChCap(*sz);
  795.       sz++;
  796.     }
  797.  
  798.     /* Here we process the eight directional commands. */
  799.  
  800.     switch (chCmd) {
  801.     case 'U': deltax =  0; deltay = -1; break;      /* Up    */
  802.     case 'D': deltax =  0; deltay =  1; break;      /* Down  */
  803.     case 'L': deltax = -1; deltay =  0; break;      /* Left  */
  804.     case 'R': deltax =  1; deltay =  0; break;      /* Right */
  805.     case 'E': deltax =  1; deltay = -1; break;      /* NorthEast */
  806.     case 'F': deltax =  1; deltay =  1; break;      /* SouthEast */
  807.     case 'G': deltax = -1; deltay =  1; break;      /* SouthWest */
  808.     case 'H': deltax = -1; deltay = -1; break;      /* NorthWest */
  809.     default: PrintError("Bad draw.");       /* Shouldn't happen. */
  810.     }
  811.     x = gi.xTurtle;
  812.     y = gi.yTurtle;
  813.     j = NFromPch(&sz)*gi.nScale;    /* Figure out how far to draw. */
  814.     if (fBlank) {
  815.       gi.xTurtle += deltax*j;
  816.       gi.yTurtle += deltay*j;
  817.     } else {
  818.       if (gs.fPS || gs.fMeta) {
  819.         gi.xTurtle += deltax*j;
  820.         gi.yTurtle += deltay*j;
  821.         DrawLine(x, y, gi.xTurtle, gi.yTurtle);
  822.       } else {
  823.         DrawPoint(gi.xTurtle, gi.yTurtle);
  824.         for (i = 0; i < j; i++) {
  825.           gi.xTurtle += deltax;
  826.           gi.yTurtle += deltay;
  827.           DrawPoint(gi.xTurtle, gi.yTurtle);
  828.         }
  829.       }
  830.       if (fNoupdate) {
  831.         gi.xTurtle = x;
  832.         gi.yTurtle = y;
  833.       }
  834.     }
  835.   }
  836. }
  837. #endif /* GRAPH */
  838.  
  839. /* xgeneral.c */
  840.